home *** CD-ROM | disk | FTP | other *** search
/ The Datafile PD-CD 1 Issue 2 / PDCD-1 - Issue 02.iso / _utilities / utilities / 001 / fue / c / ISEARCH < prev    next >
Text File  |  1991-04-03  |  24KB  |  512 lines

  1. /*
  2.  * The functions in this file implement commands that perform incremental
  3.  * searches in the forward and backward directions.  This "ISearch" command
  4.  * is intended to emulate the same command from the original EMACS 
  5.  * implementation (ITS).  Contains references to routines internal to
  6.  * SEARCH.C.
  7.  *
  8.  * REVISION HISTORY:
  9.  *
  10.  *      D. R. Banks 9-May-86
  11.  *      - added ITS EMACSlike ISearch
  12.  *
  13.  *      John M. Gamble 5-Oct-86
  14.  *      - Made iterative search use search.c's scanner() routine.
  15.  *        This allowed the elimination of bakscan().
  16.  *      - Put isearch constants into estruct.h
  17.  *      - Eliminated the passing of 'status' to scanmore() and
  18.  *        checknext(), since there were no circumstances where
  19.  *        it ever equalled FALSE.
  20.  */
  21.  
  22. #include        <stdio.h>
  23. #include        "estruct.h"
  24. #include        "etype.h"
  25. #include        "edef.h"
  26. #include        "elang.h"
  27.  
  28. #if     ISRCH
  29. /* A couple of "own" variables for re-eat */
  30. /* Hey, BLISS user, these were "GLOBAL", I made them "OWN". */
  31. static int      (PASCAL NEAR *saved_get_char)();        /* Get character routine */
  32. static int      eaten_char = -1;                /* Re-eaten char */
  33.  
  34. /* A couple more "own" variables for the command string */
  35.  
  36. static int      cmd_buff[CMDBUFLEN];            /* Save the command args here */
  37. static int      cmd_offset;                     /* Current offset into command buff */
  38. static int      cmd_reexecute = -1;             /* > 0 if re-executing command */
  39.  
  40. /* some local function type definitions */
  41.  
  42. PASCAL NEAR reeat();
  43.  
  44. /*
  45.  * Subroutine to do incremental reverse search.  It actually uses the
  46.  * same code as the normal incremental search, as both can go both ways.
  47.  */
  48.  
  49. int PASCAL NEAR risearch(f, n)
  50. {
  51.     LINE *curline;                      /* Current line on entry              */
  52.     int  curoff;                        /* Current offset on entry            */
  53.  
  54.     /* remember the initial . on entry: */
  55.  
  56.     curline = curwp->w_dotp;            /* Save the current line pointer      */
  57.     curoff  = curwp->w_doto;            /* Save the current offset            */
  58.  
  59.     /* Make sure the search doesn't match where we already are:               */
  60.  
  61.     backchar(TRUE, 1);                  /* Back up a character                */
  62.  
  63.     if (!(isearch(f, -n)))              /* Call ISearch backwards             */
  64.     {                                   /* If error in search:                */
  65.         curwp->w_dotp = curline;        /* Reset the line pointer             */
  66.         curwp->w_doto = curoff;         /*  and the offset to original value  */
  67.         curwp->w_flag |= WFMOVE;        /* Say we've moved                    */
  68.         update(FALSE);                  /* And force an update                */
  69.         mlwrite (TEXT164);      /* Say we died                        */
  70. /*               "[search failed]" */
  71.     } else mlerase ();                  /* If happy, just erase the cmd line  */
  72.     return;
  73. }
  74.  
  75. /* Again, but for the forward direction */
  76.  
  77. int PASCAL NEAR fisearch(f, n)
  78. {
  79.     LINE *curline;                      /* Current line on entry              */
  80.     int  curoff;                        /* Current offset on entry            */
  81.  
  82.     /* remember the initial . on entry: */
  83.  
  84.     curline = curwp->w_dotp;            /* Save the current line pointer      */
  85.     curoff  = curwp->w_doto;            /* Save the current offset            */
  86.  
  87.     /* do the search */
  88.  
  89.     if (!(isearch(f, n)))               /* Call ISearch forwards              */
  90.     {                                   /* If error in search:                */
  91.         curwp->w_dotp = curline;        /* Reset the line pointer             */
  92.         curwp->w_doto = curoff;         /*  and the offset to original value  */
  93.         curwp->w_flag |= WFMOVE;        /* Say we've moved                    */
  94.         update(FALSE);                  /* And force an update                */
  95.         mlwrite (TEXT164);      /* Say we died                        */
  96. /*               "[search failed]" */
  97.     } else mlerase ();                  /* If happy, just erase the cmd line  */
  98.     return;
  99. }
  100.  
  101. /*
  102.  * Subroutine to do an incremental search.  In general, this works similarly
  103.  * to the older micro-emacs search function, except that the search happens
  104.  * as each character is typed, with the screen and cursor updated with each
  105.  * new search character.
  106.  *
  107.  * While searching forward, each successive character will leave the cursor
  108.  * at the end of the entire matched string.  Typing a Control-S or Control-X
  109.  * will cause the next occurrence of the string to be searched for (where the
  110.  * next occurrence does NOT overlap the current occurrence).  A Control-R will
  111.  * change to a backwards search, META will terminate the search and Control-G
  112.  * will abort the search.  Rubout will back up to the previous match of the
  113.  * string, or if the starting point is reached first, it will delete the
  114.  * last character from the search string.
  115.  *
  116.  * While searching backward, each successive character will leave the cursor
  117.  * at the beginning of the matched string.  Typing a Control-R will search
  118.  * backward for the next occurrence of the string.  Control-S or Control-X
  119.  * will revert the search to the forward direction.  In general, the reverse
  120.  * incremental search is just like the forward incremental search inverted.
  121.  *
  122.  * In all cases, if the search fails, the user will be feeped, and the search
  123.  * will stall until the pattern string is edited back into something that
  124.  * exists (or until the search is aborted).
  125.  */
  126. PASCAL NEAR isearch(f, n)
  127. {
  128.     int                 status;         /* Search status */
  129.     int                 col;            /* prompt column */
  130.     register int        cpos;           /* character number in search string  */
  131.     register int        c;              /* current input character */
  132.     register int        expc;           /* function expanded input char       */
  133.     char                pat_save[NPAT]; /* Saved copy of the old pattern str  */
  134.     LINE                *curline;       /* Current line on entry              */
  135.     int                 curoff;         /* Current offset on entry            */
  136.     int                 init_direction; /* The initial search direction       */
  137.  
  138.     /* Initialize starting conditions */
  139.  
  140.     cmd_reexecute = -1;         /* We're not re-executing (yet?)      */
  141.     cmd_offset = 0;                     /* Start at the beginning of the buff */
  142.     cmd_buff[0] = '\0';         /* Init the command buffer            */
  143.     bytecopy (pat_save, pat, NPAT);     /* Save the old pattern string        */
  144.     curline = curwp->w_dotp;            /* Save the current line pointer      */
  145.     curoff  = curwp->w_doto;            /* Save the current offset            */
  146.     init_direction = n;                 /* Save the initial search direction  */
  147.  
  148.     /* This is a good place to start a re-execution: */
  149.  
  150. start_over:
  151.  
  152.     /* ask the user for the text of a pattern */
  153.     col = promptpattern(TEXT165);               /* Prompt, remember the col   */
  154. /*                      "ISearch: " */
  155.  
  156.     cpos = 0;                                   /* Start afresh               */
  157.     status = TRUE;                              /* Assume everything's cool   */
  158.  
  159.     /*
  160.        Get the first character in the pattern.  If we get an initial Control-S
  161.        or Control-R, re-use the old search string and find the first occurrence
  162.      */
  163.  
  164.     c = ectoc(expc = get_char());               /* Get the first character    */
  165.     if ((c == IS_FORWARD) ||
  166.         (c == IS_REVERSE) ||
  167.         (c == IS_VMSFORW))                      /* Reuse old search string?   */
  168.     {
  169.         for (cpos = 0; pat[cpos] != 0; cpos++)  /* Yup, find the length       */
  170.             col = echochar((int)pat[cpos],col); /*  and re-echo the string    */
  171.         if (c == IS_REVERSE) {                  /* forward search?            */
  172.             n = -1;                             /* No, search in reverse      */
  173.             backchar (TRUE, 1);                 /* Be defensive about EOB     */
  174.         } else
  175.             n = 1;                              /* Yes, search forward        */
  176.         status = scanmore(pat, n);              /* Do the search              */
  177.         c = ectoc(expc = get_char());           /* Get another character      */
  178.     }
  179.  
  180.     /* Top of the per character loop */
  181.                 
  182.     for (;;)                                    /* ISearch per character loop */
  183.     {
  184.         /* Check for special characters first: */
  185.         /* Most cases here change the search */
  186.  
  187.         if (expc == sterm)                      /* Want to quit searching?    */
  188.             return(TRUE);                       /* Quit searching now         */
  189.  
  190.         switch (c)                              /* dispatch on the input char */
  191.         {
  192.           case IS_ABORT:                        /* If abort search request    */
  193.             return(FALSE);                      /* Quit searching again       */
  194.  
  195.           case IS_REVERSE:                      /* If backward search         */
  196.           case IS_FORWARD:                      /* If forward search          */
  197.           case IS_VMSFORW:                      /*  of either flavor          */
  198.             if (c == IS_REVERSE)                /* If reverse search          */
  199.                 n = -1;                         /* Set the reverse direction  */
  200.             else                                /* Otherwise,                 */
  201.                 n = 1;                          /*  go forward                */
  202.             status = scanmore(pat, n);          /* Start the search again     */
  203.             c = ectoc(expc = get_char());       /* Get the next char          */
  204.             continue;                           /* Go continue with the search*/
  205.  
  206.           case IS_QUOTE:                        /* Quote character            */
  207.           case IS_VMSQUOTE:                     /*  of either variety         */
  208.             c = ectoc(expc = get_char());       /* Get the next char          */
  209.  
  210.           case IS_NEWLINE:                      /* Carriage return            */
  211.           case IS_TAB:                          /* Generically allowed        */
  212.           case '\n':                            /*  controlled characters     */
  213.             break;                              /* Make sure we use it        */
  214.  
  215.           case IS_BACKSP:                       /* If a backspace:            */
  216.           case IS_RUBOUT:                       /*  or if a Rubout:           */
  217.             if (cmd_offset <= 1)                /* Anything to delete?        */
  218.                 return(TRUE);                   /* No, just exit              */
  219.             --cmd_offset;                       /* Back up over the Rubout    */
  220.             cmd_buff[--cmd_offset] = '\0';      /* Yes, delete last char   */
  221.             curwp->w_dotp = curline;            /* Reset the line pointer     */
  222.             curwp->w_doto = curoff;             /*  and the offset            */
  223.             n = init_direction;                 /* Reset the search direction */
  224.             bytecopy (pat, pat_save, NPAT);     /* Restore the old search str */
  225.             cmd_reexecute = 0;                  /* Start the whole mess over  */
  226.             goto start_over;                    /* Let it take care of itself */
  227.  
  228.           /* Presumably a quasi-normal character comes here */
  229.  
  230.           default:                              /* All other chars            */
  231.             if (c < ' ')                        /* Is it printable?           */
  232.             {                                   /* Nope.                      */
  233.                 reeat(c);                       /* Re-eat the char            */
  234.                 return(TRUE);                   /* And return the last status */
  235.             }
  236.         }  /* Switch */
  237.  
  238.         /* I guess we got something to search for, so search for it           */
  239.  
  240.         pat[cpos++] = c;                        /* put the char in the buffer */
  241.         if (cpos >= NPAT)                       /* too many chars in string?  */
  242.         {                                       /* Yup.  Complain about it    */
  243.             mlwrite(TEXT166);
  244. /*                  "? Search string too long" */
  245.             return(TRUE);                       /* Return an error            */
  246.         }
  247.         pat[cpos] = 0;                          /* null terminate the buffer  */
  248.         col = echochar(c,col);                  /* Echo the character         */
  249.         if (!status) {                          /* If we lost last time       */
  250.             TTputc(BELL);               /* Feep again                 */
  251.             TTflush();                  /* see that the feep feeps    */
  252.         } else                                  /* Otherwise, we must have won*/
  253.             if (!(status = checknext(c, pat, n))) /* See if match             */
  254.                 status = scanmore(pat, n);      /*  or find the next match    */
  255.         c = ectoc(expc = get_char());           /* Get the next char          */
  256.     } /* for {;;} */
  257. }
  258.  
  259. /*
  260.  * Trivial routine to insure that the next character in the search string is
  261.  * still true to whatever we're pointing to in the buffer.  This routine will
  262.  * not attempt to move the "point" if the match fails, although it will 
  263.  * implicitly move the "point" if we're forward searching, and find a match,
  264.  * since that's the way forward isearch works.
  265.  *
  266.  * If the compare fails, we return FALSE and assume the caller will call
  267.  * scanmore or something.
  268.  */
  269.  
  270. int PASCAL NEAR checknext(chr, patrn, dir) /* Check next character in search string */
  271. int     chr;                    /* Next char to look for                 */
  272. char    *patrn;                 /* The entire search string (incl chr)   */
  273. int     dir;                    /* Search direction                      */
  274. {
  275.     register LINE *curline;             /* current line during scan           */
  276.     register int curoff;                /* position within current line       */
  277.     register int buffchar;              /* character at current position      */
  278.     int status;                         /* how well things go                 */
  279.  
  280.  
  281.     /* setup the local scan pointer to current "." */
  282.  
  283.     curline = curwp->w_dotp;            /* Get the current line structure     */
  284.     curoff  = curwp->w_doto;            /* Get the offset within that line    */
  285.  
  286.     if (dir > 0)                        /* If searching forward               */
  287.     {
  288.         if (curoff == llength(curline)) /* If at end of line                  */
  289.         {
  290.             curline = lforw(curline);   /* Skip to the next line              */
  291.             if (curline == curbp->b_linep)
  292.                 return(FALSE);          /* Abort if at end of buffer          */
  293.             curoff = 0;                 /* Start at the beginning of the line */
  294.             buffchar = '\r';            /* And say the next char is NL        */
  295.         } else
  296.             buffchar = lgetc(curline, curoff++); /* Get the next char         */
  297.         if (status = eq(buffchar, chr)) /* Is it what we're looking for?      */
  298.         {
  299.             curwp->w_dotp = curline;    /* Yes, set the buffer's point        */
  300.             curwp->w_doto = curoff;     /*  to the matched character          */
  301.             curwp->w_flag |= WFMOVE;    /* Say that we've moved               */
  302.         }
  303.         return(status);         /* And return the status              */
  304.     } else                              /* Else, if reverse search:           */
  305.         return(match_pat (patrn));      /* See if we're in the right place    */
  306. }
  307.  
  308. /*
  309.  * This hack will search for the next occurrence of <pat> in the buffer, either
  310.  * forward or backward.  It is called with the status of the prior search
  311.  * attempt, so that it knows not to bother if it didn't work last time.  If
  312.  * we can't find any more matches, "point" is left where it was before.  If
  313.  * we do find a match, "point" will be at the end of the matched string for
  314.  * forward searches and at the beginning of the matched string for reverse
  315.  * searches.
  316.  */
  317.  
  318. int PASCAL NEAR scanmore(patrn, dir)    /* search forward or back for a pattern       */
  319. char    *patrn;                 /* string to scan for                         */
  320. int     dir;                    /* direction to search                        */
  321. {
  322.         int     sts;                    /* search status                      */
  323.  
  324.         setjtable(patrn);               /* Set up fast search arrays  */
  325.  
  326.         if (dir < 0)                            /* reverse search?            */
  327.                 sts = scanner(tap, REVERSE, PTBEG);
  328.         else
  329.                 sts = scanner(patrn, FORWARD, PTEND);   /* Nope. Go forward   */
  330.  
  331.         if (!sts)
  332.         {
  333.                 TTputc(BELL);   /* Feep if search fails       */
  334.                 TTflush();              /* see that the feep feeps    */
  335.         }
  336.  
  337.         return(sts);                            /* else, don't even try       */
  338. }
  339.  
  340. /*
  341.  * The following is a worker subroutine used by the reverse search.  It
  342.  * compares the pattern string with the characters at "." for equality. If
  343.  * any characters mismatch, it will return FALSE.
  344.  *
  345.  * This isn't used for forward searches, because forward searches leave "."
  346.  * at the end of the search string (instead of in front), so all that needs to
  347.  * be done is match the last char input.
  348.  */
  349.  
  350. int PASCAL NEAR match_pat (patrn) /* See if the pattern string matches string at "."   */
  351. char    *patrn;         /* String to match to buffer                         */
  352. {
  353.     register int  i;                    /* Generic loop index/offset          */
  354.     register int buffchar;              /* character at current position      */
  355.     register LINE *curline;             /* current line during scan           */
  356.     register int curoff;                /* position within current line       */
  357.  
  358.     /* setup the local scan pointer to current "." */
  359.  
  360.     curline = curwp->w_dotp;            /* Get the current line structure     */
  361.     curoff  = curwp->w_doto;            /* Get the offset within that line    */
  362.  
  363.     /* top of per character compare loop: */
  364.  
  365.     for (i = 0; i < strlen(patrn); i++) /* Loop for all characters in patrn   */
  366.     {
  367.         if (curoff == llength(curline)) /* If at end of line                  */
  368.         {
  369.             curline = lforw(curline);   /* Skip to the next line              */
  370.             curoff = 0;                 /* Start at the beginning of the line */
  371.             if (curline == curbp->b_linep)
  372.                 return(FALSE);          /* Abort if at end of buffer          */
  373.             buffchar = '\r';            /* And say the next char is NL        */
  374.         } else
  375.             buffchar = lgetc(curline, curoff++); /* Get the next char         */
  376.         if (!eq(buffchar, patrn[i]))    /* Is it what we're looking for?      */
  377.             return(FALSE);              /* Nope, just punt it then            */
  378.     }
  379.     return(TRUE);                       /* Everything matched? Let's celebrate*/
  380. }
  381.  
  382. /* Routine to prompt for I-Search string. */
  383.  
  384. int PASCAL NEAR promptpattern(prompt)
  385. char *prompt;
  386. {
  387.     char tpat[NPAT+20];
  388.  
  389.     strcpy(tpat, prompt);               /* copy prompt to output string */
  390.     strcat(tpat, " [");                 /* build new prompt string */
  391.     expandp(pat, &tpat[strlen(tpat)], NPAT/2);  /* add old pattern */
  392.     strcat(tpat, "]<META>: ");
  393.  
  394.     /* check to see if we are executing a command line */
  395.     if (!clexec) {
  396.         mlwrite(tpat);
  397.     }
  398.     return(strlen(tpat));
  399. }
  400.  
  401. /* routine to echo i-search characters */
  402.  
  403. int PASCAL NEAR echochar(c,col)
  404.  
  405. int c;          /* character to be echoed */
  406. int col;        /* column to be echoed in */
  407.  
  408. {
  409.     movecursor(term.t_nrow,col);                /* Position the cursor        */
  410.     if ((c < ' ') || (c == 0x7F))               /* Control character?         */
  411.     {
  412.         switch (c)                              /* Yes, dispatch special cases*/
  413.         {
  414.           case '\r':                            /* Newline                    */
  415.             mlout('<');
  416.             mlout('N');
  417.             mlout('L');
  418.             mlout('>');
  419.             col += 3;
  420.             break;
  421.  
  422.           case '\t':                            /* Tab                        */
  423.             mlout('<');
  424.             mlout('T');
  425.             mlout('A');
  426.             mlout('B');
  427.             mlout('>');
  428.             col += 4;
  429.             break;
  430.  
  431.           case 0x7F:                            /* Rubout:                    */
  432.             mlout('^');         /* Output a funny looking     */
  433.             mlout('?');         /*  indication of Rubout      */
  434.             col++;                              /* Count the extra char       */
  435.             break;
  436.  
  437.           default:                              /* Vanilla control char       */
  438.             mlout('^');         /* Yes, output prefix         */
  439.             mlout(c+0x40);              /* Make it "^X"               */
  440.             col++;                              /* Count this char            */
  441.         }
  442.     } else
  443.         mlout(c);                       /* Otherwise, output raw char */
  444.     TTflush();                          /* Flush the output           */
  445.     return(++col);                              /* return the new column no   */
  446. }
  447.  
  448. /*
  449.  * Routine to get the next character from the input stream.  If we're reading
  450.  * from the real terminal, force a screen update before we get the char. 
  451.  * Otherwise, we must be re-executing the command string, so just return the
  452.  * next character.
  453.  */
  454.  
  455. int PASCAL NEAR get_char ()
  456. {
  457.     int c;                              /* A place to get a character         */
  458.  
  459.     /* See if we're re-executing: */
  460.  
  461.     if (cmd_reexecute >= 0)             /* Is there an offset?                */
  462.         if ((c = cmd_buff[cmd_reexecute++]) != 0)
  463.             return(c);                  /* Yes, return any character          */
  464.  
  465.     /* We're not re-executing (or aren't any more).  Try for a real char      */
  466.  
  467.     cmd_reexecute = -1;         /* Say we're in real mode again       */
  468.     update(FALSE);                      /* Pretty up the screen               */
  469.     if (cmd_offset >= CMDBUFLEN-1)      /* If we're getting too big ...       */
  470.     {
  471.         mlwrite (TEXT167);      /* Complain loudly and bitterly       */
  472. /*               "? command too long" */
  473.         return(sterm);                  /* And force a quit                   */
  474.     }
  475.     c = getkey();               /* Get the next character             */
  476.     cmd_buff[cmd_offset++] = c; /* Save the char for next time        */
  477.     cmd_buff[cmd_offset] = '\0';/* And terminate the buffer           */
  478.     return(c);                          /* Return the character               */
  479. }
  480.  
  481. /*
  482.  * Hacky routine to re-eat a character.  This will save the character to be
  483.  * re-eaten by redirecting the input call to a routine here.  Hack, etc.
  484.  */
  485.  
  486. /* Come here on the next term.t_getchar call: */
  487.  
  488. int PASCAL NEAR uneat()
  489. {
  490.     int c;
  491.  
  492.     term.t_getchar = saved_get_char;    /* restore the routine address        */
  493.     c = eaten_char;                     /* Get the re-eaten char              */
  494.     eaten_char = -1;                    /* Clear the old char                 */
  495.     return(c);                          /* and return the last char           */
  496. }
  497.  
  498. PASCAL NEAR reeat(c)
  499. int     c;
  500. {
  501.     if (eaten_char != -1)               /* If we've already been here         */
  502.         return/*(NULL)*/;               /* Don't do it again                  */
  503.     eaten_char = c;                     /* Else, save the char for later      */
  504.     saved_get_char = term.t_getchar;    /* Save the char get routine          */
  505.     term.t_getchar = uneat;             /* Replace it with ours               */
  506. }
  507. #else
  508. PASCAL NEAR isearch()
  509. {
  510. }
  511. #endif
  512.